package com.xuexuehai.bkm.core.configuration.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.xuexuehai.bkm.core.common.constants.RespConstants;
import com.xuexuehai.bkm.core.common.exception.security.InvalidUserException;
import com.xuexuehai.bkm.core.common.exception.security.UserNotLoginException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author xuexuehai
 * @mailBox xuehai.xue@QQ.com
 * @date 2021/9/12 3:08 下午
 */
@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //拦截前端回传的token
        String token = request.getHeader("token");
        log.info("拦截到的Token --->" + token);

        //从redis获取token
        String tokenInRedis = stringRedisTemplate.opsForValue().get(token);
        log.info("从redis中以token为key获取的audience ---> " + tokenInRedis);

        //如果redis中没有取得token，则抛出用户未登录异常
        if(tokenInRedis == null){
            throw new UserNotLoginException(RespConstants.HAS_NOT_LOGIN);
        }

        //token存在，验证用户名和密码
        String username = JWT.decode(token).getAudience().get(0);

        String passwordInRedis = tokenInRedis.split("-")[1];
        String usernameInRedis = tokenInRedis.split("-")[0];

        if(!(username.equals(usernameInRedis))){
            throw new InvalidUserException("用户名在传输过程中可能被篡改");
        }

        //验证redis中的密码和传入的密码是否相同，需要将redis中的密码进行JWT编码，然后进行验证
        JWTVerifier build = JWT.require(Algorithm.HMAC256(passwordInRedis)).build();
        //验证不通过抛出的JWTVerificationException会被AuthorityExceptionHandler中的TokenHandler拦截并返回
        build.verify(token);

        return true;
    }
}
